package br.org.acessobrasil.portal.controle;
 
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.TreeMap;
 
import org.apache.log4j.Logger;
 
import br.org.acessobrasil.ases.ferramentas_de_reparo.controle.XHTMLControle;
import br.org.acessobrasil.ases.nucleo.InterfClienteDoNucleo;
import br.org.acessobrasil.ases.nucleo.NucleoEstruturado;
import br.org.acessobrasil.ases.nucleo.adapters.entidade.ArmazenaErroOuAviso;
import br.org.acessobrasil.ases.regras.RegrasHardCodedEmag;
import br.org.acessobrasil.portal.modelo.ConteudoFormatado;
import br.org.acessobrasil.portal.modelo.ErrosRegra;
import br.org.acessobrasil.portal.modelo.RelatorioAcessibilidade;
 
/**
 * Cliente do ASES para validacao de acessibilidade
 * @author Fabio Issamu Oshiro
 *
 */
public class AsesCliente implements InterfClienteDoNucleo{
    private static Logger logger = Logger.getLogger(AsesCliente.class);
     
    private static final ResourceBundle AsesEmag = ResourceBundle.getBundle("AsesEmag");
     
    private int correcaoLinha = 0;
    private NucleoEstruturado nucleo;
    private RegrasHardCodedEmag regras = new RegrasHardCodedEmag();
    private boolean errado=false;
    private RelatorioAcessibilidade relatorio;
    private ArrayList<ErrosRegra> listErroP1 = new ArrayList<ErrosRegra>();
    private ArrayList<ErrosRegra> listErroP2 = new ArrayList<ErrosRegra>();
    private ArrayList<ErrosRegra> listErroP3 = new ArrayList<ErrosRegra>();
    private ArrayList<ErrosRegra> listErroXHTML = new ArrayList<ErrosRegra>();
    private ArrayList<ErrosRegra> listAvisoP1 = new ArrayList<ErrosRegra>();
    private ArrayList<ErrosRegra> listAvisoP2 = new ArrayList<ErrosRegra>();
    private ArrayList<ErrosRegra> listAvisoP3 = new ArrayList<ErrosRegra>();
     
    private List<ArmazenaErroOuAviso> avisoP1;
    private List<ArmazenaErroOuAviso> avisoP2;
    private List<ArmazenaErroOuAviso> avisoP3;
    private List<ArmazenaErroOuAviso> avisoXHTML=new ArrayList<ArmazenaErroOuAviso>();
    private List<ArmazenaErroOuAviso> erroP1;
    private List<ArmazenaErroOuAviso> erroP2;
    private List<ArmazenaErroOuAviso> erroP3;
    private List<ArmazenaErroOuAviso> erroXHTML=new ArrayList<ArmazenaErroOuAviso>();
    private static String dtdPath;
    private static boolean dtdPathVerificado=false;
    static {
        //Configura o idioma do ases
        br.org.acessobrasil.silvinha.util.lang.TokenLang.LANG="pt";
    }
 
     
     
    public AsesCliente(){
        nucleo = new NucleoEstruturado();
        nucleo.addCliente(this);     
    }
     
    /**
     * Verifica o path do DTD uma vez
     */
    private static void checkDtdPath(){
        if(dtdPathVerificado) return;
        dtdPathVerificado = true;
        if(!(dtdPath.equals("default") || dtdPath.equals(""))){
            //caso nao seja o default verificar se existe o DTD
            try{
                URL dtdUrl = new URL(dtdPath+"/xhtml1-strict.dtd");
                dtdUrl.openConnection().getContent();
            }catch(Exception e){
                logger.error("O dtdBasePath "+dtdPath+" na applicationContext.xml esta incorreto.",e);
            }
        }
    }
    /**
     * Configura o caminho do path
     * Ex.: http://www.seusite.com.br/dtd
     * @param path
     */
    public void setDtdBasePath(String path){
        if(path.equals("default") || path.equals("")){
            XHTMLControle.XHTML_01Strict="<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">";
        }else{
            //Configura o local dos DTDs
            XHTMLControle.XHTML_01Strict="<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \""+path+"/xhtml1-strict.dtd\">";
            XHTMLControle.HTML4_01Strict="<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Strict//EN\" \""+path+"/html4.01strict.dtd\">";
            XHTMLControle.HTML4_01Transitional="<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \""+path+"/html4.01transitional.dtd\">";
            XHTMLControle.HTML4_01Frameset="<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML Frameset//EN\" \""+path+"/html4.01frameset.dtd\">";
        }
        dtdPath = path;
    }
     
    /**
     * Fecha as tags (br,img)
     * @param conteudoFormatado
     */
    public void tratar(ConteudoFormatado conteudoFormatado){
        conteudoFormatado.setDeConteudo(tratar(conteudoFormatado.getDeConteudo()));
    }
    /**
     * Fecha as tags (br,img)
     * Coloca as tags em min�sculas
     * @param conteudoFormatado
     */
    public String tratar(String conteudo){
        String retorno = conteudo
            .replaceAll("<img\\s([^>]*[^/])>","<img $1 />")
            .replace("<br>", "<br />")
            .replaceAll("</li>(&nbsp;|\\s)(&nbsp;|\\s)*<li>", "</li><li>") 
            .replaceAll("<ul>(&nbsp;|\\s)(&nbsp;|\\s)*<li>", "<ul><li>");
        return retorno;
    }
     
    /**
     * Avalia o padr&atilde;o html em lowerCase <br>
     * Avalia a Acessibilidade com casesensitive <br>
     * 
     * @param html c&oacute;digo html
     */
    public void avalia(String html){
        checkDtdPath();
        nucleo.setRegras(regras);
        nucleo.setCodHTML(html);
        nucleo.setAvaliaCSS(false);
        try {
            //nucleo.setCodWcagEmag(NucleoEstruturado.EMAG);
            nucleo.setWCAGEMAG(NucleoEstruturado.EMAG);
        } catch (RuntimeException e) {
            e.printStackTrace();
        }
        nucleo.avalia();         
        reavalia(html.replace("&","&amp;"));
    }
    /**
     * Organiza o array armazenaErroOuAviso
     * por ErrosRegra
     */
    private void organizaErroOuAviso(ArrayList<ErrosRegra> lista,ArrayList<ArmazenaErroOuAviso> armazenaErroOuAviso){
        //Coloca no hashmap de forma ordenada
        HashMap<String,ArrayList<ArmazenaErroOuAviso>> mapa = new HashMap<String,ArrayList<ArmazenaErroOuAviso>>();
        for(int i=0;i<armazenaErroOuAviso.size();i++){
            if(!mapa.containsKey(armazenaErroOuAviso.get(i).getIdTextoRegra())){
                mapa.put(armazenaErroOuAviso.get(i).getIdTextoRegra(), new ArrayList<ArmazenaErroOuAviso>());                 
            }
            armazenaErroOuAviso.get(i).setLinha(armazenaErroOuAviso.get(i).getLinha()+correcaoLinha);
            mapa.get(armazenaErroOuAviso.get(i).getIdTextoRegra())
            .add(armazenaErroOuAviso.get(i));
        }
        Map<String, ArrayList<ArmazenaErroOuAviso>> sortedMap = new TreeMap<String, ArrayList<ArmazenaErroOuAviso>>(mapa);
        List<String> mapKeys = new ArrayList<String>(sortedMap.keySet());
        ArrayList<ArrayList<ArmazenaErroOuAviso>> mapValues = new ArrayList<ArrayList<ArmazenaErroOuAviso>>(sortedMap.values());
        for(int i=0;i<mapKeys.size();i++){
            ErrosRegra errosRegra = new ErrosRegra();
            errosRegra.setCodigo(mapKeys.get(i));
            errosRegra.setTexto(getTextoRegra("r"+mapKeys.get(i)));
            errosRegra.setListOcorrencia(mapValues.get(i));
            lista.add(errosRegra);             
        }
    }
     
    public void avisosP1(ArrayList<ArmazenaErroOuAviso> armazenaErroOuAviso) {
        avisoP1 = armazenaErroOuAviso;
        organizaErroOuAviso(listAvisoP1, armazenaErroOuAviso);         
    }
 
    private void reavalia(String codigo) {
        logger.debug("reavalia iniciado...");
        try {
            XHTMLControle xhtmlValidator = new XHTMLControle();
            try {
                xhtmlValidator.avalia(codigo);
            } catch (RuntimeException e) {
                logger.warn("Erro ao validar o xhtml: " + e.getMessage());
            } catch(Exception e){
                logger.warn("Erro ao validar o xhtml: " + e.getMessage());
            }
            for (int i = 0; i < xhtmlValidator.length(); i++) {
                ErrosRegra errosRegra1=new ErrosRegra();
                 
                errosRegra1.setTexto(xhtmlValidator.getMensagem(i));
             
                List<ArmazenaErroOuAviso> armazenaErrosOuAvisos=new ArrayList<ArmazenaErroOuAviso>();
                ArmazenaErroOuAviso armazenaErroOuAviso=new ArmazenaErroOuAviso();
                armazenaErroOuAviso.setLinha(xhtmlValidator.getLinha(i));
                armazenaErroOuAviso.setColuna(xhtmlValidator.getColuna(i));
                avisoXHTML.add(armazenaErroOuAviso);
                erroXHTML.add(armazenaErroOuAviso);                 
                armazenaErrosOuAvisos.add(armazenaErroOuAviso);
                errosRegra1.setListOcorrencia(armazenaErrosOuAvisos);
             
                listErroXHTML.add(errosRegra1);
                //retirado por bug em DTD
                errado=true;
            }
            getRelatorio();
            // domValidator
        } catch (Exception e) {
            logger.warn("Erro ao reavaliar: " + e.getMessage());
        }
        logger.debug("reavalia terminado");
    }
     
    public void avisosP2(ArrayList<ArmazenaErroOuAviso> armazenaErroOuAviso) {
        avisoP2 = armazenaErroOuAviso;
        organizaErroOuAviso(listAvisoP2, armazenaErroOuAviso);
    }
 
     
    public void avisosP3(ArrayList<ArmazenaErroOuAviso> armazenaErroOuAviso) {
        avisoP3 = armazenaErroOuAviso;
        organizaErroOuAviso(listAvisoP3, armazenaErroOuAviso);         
    }
 
     
    public void errosP1(ArrayList<ArmazenaErroOuAviso> armazenaErroOuAviso) {
        if(armazenaErroOuAviso!=null && armazenaErroOuAviso.size()>0){
            errado=true;
        }
        erroP1=armazenaErroOuAviso;
        organizaErroOuAviso(listErroP1, armazenaErroOuAviso);
    }
 
     
    public void errosP2(ArrayList<ArmazenaErroOuAviso> armazenaErroOuAviso) {
        //remover a regra 2.10
        for(int i=armazenaErroOuAviso.size()-1;i>=0;i--){
            ArmazenaErroOuAviso erro = armazenaErroOuAviso.get(i);
            if(erro.getIdTextoRegra().equals("2.10")){
                armazenaErroOuAviso.remove(i);
            }
        }
        if(armazenaErroOuAviso!=null && armazenaErroOuAviso.size()>0){
            errado=true;
        }
        erroP2=armazenaErroOuAviso;
        organizaErroOuAviso(listErroP2, armazenaErroOuAviso);
    }
 
     
    public void errosP3(ArrayList<ArmazenaErroOuAviso> armazenaErroOuAviso) {
        if(armazenaErroOuAviso!=null && armazenaErroOuAviso.size()>0){
            errado=true;
        }
        erroP3=armazenaErroOuAviso;
        organizaErroOuAviso(listErroP3, armazenaErroOuAviso);
         
    }
 
     
    public void fimDaAvaliacao() {
         
    }
 
    public RelatorioAcessibilidade getRelatorio(){
        //gerar o relatorio
        if(relatorio!=null) return relatorio;
        relatorio = new RelatorioAcessibilidade();
        relatorio.setListAvisoP1(listAvisoP1);
        relatorio.setListAvisoP2(listAvisoP2);
        relatorio.setListAvisoP3(listAvisoP3);
     
        relatorio.setListErroP1(listErroP1);
        relatorio.setListErroP2(listErroP2);
        relatorio.setListErroP3(listErroP3);
        relatorio.setListErroXHTML(listErroXHTML);
         
        //imprimeListaErr(listErroP1);
        //imprimeListaErr(listErroP2);
        //imprimeListaErr(listErroP3);
        return relatorio;
    }
     
    public void linksAchadosPeloNucleo(HashSet<String> arg0) {}
    public boolean isErrado() {
        return errado;
    }
    public List<ArmazenaErroOuAviso> getListErroP1() {
        return erroP1;
    }
    public List<ArmazenaErroOuAviso> getListErroP2() {
        return erroP2;
    }
    public List<ArmazenaErroOuAviso> getListErroP3() {
        return erroP3;
    }
    public RegrasHardCodedEmag getRegras() {
        return regras;
    }
    public List<ArmazenaErroOuAviso> getAvisoP1() {
        return avisoP1;
    }
    public List<ArmazenaErroOuAviso> getAvisoP2() {
        return avisoP2;
    }
    public List<ArmazenaErroOuAviso> getAvisoP3() {
        return avisoP3;
    }
    public void setCorrecaoLinha(int i) {
        correcaoLinha = i;
    }
     
    public List<ArmazenaErroOuAviso> getErroXHTML() {
        return erroXHTML;
    }
    public static String getTextoRegra(String key) {
        try {
            return AsesEmag.getString(key);
        } catch (MissingResourceException e) {
            return '!' + key + '!';
        }
    }
} 